/*
 * Copyright (c) 2011 Imre Fazekas.
 *  All rights reserved.
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 *  Neither the name of the Brillien nor the names of its
 *  terms and concepts may be used to endorse or promote products derived from this
 *  software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

package com.vii.brillien.services.websocket.applications.purity;

import com.vii.brillien.ignition.transport.BrillienCommunication;
import com.vii.brillien.kernel.BrillienException;
import com.vii.brillien.kernel.axiom.transport.Communication;
import com.vii.brillien.services.websocket.BrillienMediatorWebSocketApplication;
import com.vii.streamline.json.JsonRpcError;
import com.vii.streamline.json.JsonRpcServices;
import com.vii.streamline.services.IOServices;
import com.vii.streamline.services.error.StreamLineException;
import com.vii.streamline.services.json.JsonServices;
import org.glassfish.grizzly.websockets.ProtocolHandler;
import org.glassfish.grizzly.websockets.WebSocket;
import org.glassfish.grizzly.websockets.WebSocketListener;

import static com.vii.streamline.services.json.JsonServices.toJSON;

public class PureWebSocketApplication extends BrillienMediatorWebSocketApplication {

    protected static PureWebSocketApplication   webSocketApplicationInstance;

    static {
        init();
    }

    private static void init(){
        if( webSocketApplicationInstance == null )
            webSocketApplicationInstance = new PureWebSocketApplication();
    }

    public static PureWebSocketApplication getWebSocketApplicationInstance() {
        init();

        return webSocketApplicationInstance;
    }

    public PureWebSocket newBrillienMessageWebSocket(ProtocolHandler handler, WebSocketListener... listeners){
        return new PureWebSocket( handler, listeners );
    }
    
    @Override
    public WebSocket createSocket(ProtocolHandler handler, WebSocketListener... listeners) {
        return newBrillienMessageWebSocket(handler, listeners);
    }
    /*
    @Override
    public void onClose(org.glassfish.grizzly.websockets.WebSocket socket, org.glassfish.grizzly.websockets.DataFrame frame) {
        PureWebSocket bmSocket = (PureWebSocket)socket;
    }

    @Override
    public void onConnect(org.glassfish.grizzly.websockets.WebSocket socket) {
        PureWebSocket bmSocket = (PureWebSocket)socket;
    }
    */

    protected void sendResponseMessage(WebSocket socket, BrillienCommunication comm, boolean needtoCompress) throws StreamLineException, BrillienException {
        System.out.println("Sending response:::" + comm);
        if( needtoCompress )
            socket.send( IOServices.gzipCompress(toJSON(comm)) );
        else
            socket.send( toJSON(comm) );
    }
    protected void sendErrorMessage(WebSocket socket, JsonRpcError error, boolean needtoCompress) throws StreamLineException {
        sendErrorMessage( socket, error.getCode(), error.getMessage(), error.getData(), needtoCompress );
    }
    protected void sendErrorMessage(WebSocket socket, int errorCode, String errorMessage, Object payload, boolean needtoCompress) throws StreamLineException {
        sendErrorMessage( null, socket, errorCode, errorMessage, payload, needtoCompress );
    }
    protected void sendErrorMessage(BrillienCommunication comm, WebSocket socket, int errorCode, String errorMessage, Object payload, boolean needtoCompress) throws StreamLineException {
        BrillienCommunication msg = null;

        try{
            msg = comm != null
                    ? comm.prepareForError( errorCode, errorMessage, payload )
                    : new BrillienCommunication( errorCode, errorMessage, payload );
        } catch (BrillienException e) {
            msg = new BrillienCommunication( errorCode, errorMessage, payload );
        }

        if( payload != null )
            msg.setErrorValue( toJSON(payload) );

        if( needtoCompress )
            socket.send( IOServices.gzipCompress(toJSON(msg)) );
        else
            socket.send( toJSON(msg) );
    }

    protected void bindWebSocket(PureWebSocket webSocket, BrillienCommunication comm) throws BrillienException{

    }

    protected void checkMessage(BrillienCommunication msg) throws BrillienException{
        if( msg == null )
            throw new BrillienException( StreamLineException.ERROR_INVALID_REQUEST, "No message content has been found!" );

        if( msg.getId() == null || msg.getSender() == null || msg.getRecipient() == null || msg.getMessageType() == Communication.TYPE_UNDEFINED ||
            msg.getMessageType() == Communication.TYPE_UNKOWN || msg.getSubject() == null
        )
            throw new BrillienException( StreamLineException.ERROR_INVALID_REQUEST, "Message content is invalid!" );
    }

    public void onMessage(WebSocket socket, String request, boolean needCompress) {
        BrillienCommunication msg = null;
        try {
            msg = JsonServices.parseJSON( request, BrillienCommunication.class );

            checkMessage( msg );

            if( msg.getMessageType() == Communication.TYPE_AGET || msg.getMessageType() == Communication.TYPE_UNDEFINED )
                throw new BrillienException( StreamLineException.ERROR_INVALID_REQUEST, "Server is not accepting such message type:", msg.getMessageType() );

            checkCallPermissions( msg );

            checkCallPreconditions( msg );

            bindWebSocket( (PureWebSocket)socket, msg );

            msg.setSender( mediator.mediatorEntity() );
            msg.addExtension( "needCompresss", needCompress );

            BrillienCommunication response = mediator.sendCommunication( msg );

            if( response != null )
                sendResponseMessage( socket, response, needCompress );
        } catch (StreamLineException e) {
            e.printStackTrace();
            if( socket.isConnected() )
                try {
                    sendErrorMessage( msg, socket, e.getErrorCode(), e.getMessage(), e.getPayLoad(), needCompress );
                } catch (StreamLineException e1) { e.printStackTrace(); }
        } catch (BrillienException e) {
            e.printStackTrace();
            if( socket.isConnected() )
                try {
                    sendErrorMessage( msg, socket, e.getErrorCode(), e.getMessage(), e.getValue(), needCompress );
                } catch (StreamLineException e1) { e.printStackTrace(); }
        } catch (Exception e) {
            e.printStackTrace();
            if( socket.isConnected() )
                try {
                    sendErrorMessage( msg, socket, StreamLineException.ERROR_SERVER_ERROR, "Some error occurred.", e.getMessage(), needCompress );
                } catch (StreamLineException e1) { e.printStackTrace(); }
        }
    }
    public void onMessage(WebSocket socket, String request) {
        onMessage( socket, request, false );
    }

    @Override
    public void onMessage(WebSocket socket, byte[] bytes) {
        String request = null;
        try {
            request = IOServices.gzipDecompress(bytes);
        } catch (StreamLineException e) {
            e.printStackTrace();
            if( socket.isConnected() )
                try {
                    sendErrorMessage( socket, JsonRpcServices.ERROR_INVALID_REQUEST, true );
                } catch (StreamLineException e1) { }
        }
        onMessage( socket, request, true );
    }

}
